//
// $Id$

package zachg.survival;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Rectangle;

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;

import java.util.HashMap;

import com.samskivert.swing.Label;
import com.threerings.media.VirtualMediaPanel;
import com.threerings.media.animation.AnimationArranger;
import com.threerings.toybox.util.ToyBoxContext;
import com.threerings.util.Name;

import com.threerings.crowd.client.PlaceView;
import com.threerings.crowd.data.PlaceObject;

import com.threerings.parlor.media.ScoreAnimation;
import com.threerings.presents.dobj.AttributeChangeListener;
import com.threerings.presents.dobj.AttributeChangedEvent;
import com.threerings.presents.dobj.EntryAddedEvent;
import com.threerings.presents.dobj.EntryUpdatedEvent;
import com.threerings.presents.dobj.EntryRemovedEvent;
import com.threerings.presents.dobj.SetListener;

/**
 * Displays the main game interface (the board).
 */
@SuppressWarnings({"serial","rawtypes", "deprecation"})
public class SurvivalBoardView extends VirtualMediaPanel
    implements PlaceView, SetListener, AttributeChangeListener
{
    /**
     * Constructs a view which will initialize itself and prepare to display
     * the game board.
     */
    public SurvivalBoardView (ToyBoxContext ctx, SurvivalController ctrl)
    {
    	super(ctx.getFrameManager());
    	_ctx = ctx;
        _ctrl = ctrl;
        
        // create our logic class
        _logic = new SurvivalLogic(SurvivalObject.GAME_SIZE);
                
        // listen for mouse motion and presses
        addMouseListener(new MouseAdapter() {
        	public void mousePressed (MouseEvent e) {
                SurvivalObject.GameCharacter cursorGameCharacter = _cursor.getGameCharacter();
                _nextMoveSprite._comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f);
                SurvivalObject.GameCharacter characterAtPoint = _gameobj.getCharacterAtLocation(cursorGameCharacter.x, cursorGameCharacter.y);
                SurvivalObject.GameCharacter playerCharacter =  _gameobj.getGameCharacterFromName(_ctx.getClient().getClientObject().username);

                if (	_logic.isLegalMove(cursorGameCharacter,playerCharacter) == true &&
                		(characterAtPoint == null || characterAtPoint.playerOwner.equals(playerCharacter.playerOwner) == true)
                	){  
            		//move the placeholder sprite to the cursor's location
                	_nextMoveSprite.setPosition(cursorGameCharacter.x,cursorGameCharacter.y, true);
            		
            		//we use a temp character here with the player's name 
            		//because that will allow us to make sure false results 
            		//don't come out of the canMine or canAttack methods in SurvivalLogic 
            		SurvivalObject.GameCharacter tempCharacter = _nextMoveSprite._character;
            		tempCharacter.playerOwner = playerCharacter.playerOwner;
            		updateSidePanelOptions(tempCharacter);
                }
            }
        });
        // listen for mouse motion
        addMouseMotionListener(new MouseMotionAdapter() {
            public void mouseMoved (MouseEvent e) {
            	//Dirty hack to force a redraw on the whole board. Encountered some issues with mines not showing up, this is the fix.
            	//Best solution would be for me to dive deeper in the application flow behind the scenes, 
            	//determine when things get drawn and what the problem there is. ZERO time to do that, and no debugger makes it a nightmare.
                getSpriteManager().getRegionManager().invalidateRegion(0, 0, getSize().width, getSize().height);        
            	
                int tx = e.getX() / GameCharacterSprite.SIZE;
                int ty = e.getY() / GameCharacterSprite.SIZE;
                SurvivalObject.GameCharacter cursorGameCharacter = _cursor.getGameCharacter();
                SurvivalObject.GameCharacter characterAtCursor = _gameobj.getCharacterAtLocation(_cursor.getGameCharacter().x, _cursor.getGameCharacter().y);
                showUnitInfo(characterAtCursor);
                _cursor.setPosition(
                		tx,
                		ty,
                		_logic.isLegalMove(cursorGameCharacter, _gameobj.getGameCharacterFromName(_ctx.getClient().getClientObject().username) )
                );
            }
        });
    }
  
    /**
     * Activates "placing" mode which allows the user to place a piece of the
     * specified color.
     */
    public void setPlacingMode (int color)
    {

    	if (color != -1) {
            addSprite(_cursor);
            addSprite(_nextMoveSprite);
        } else if ( isManaged(_cursor) && isManaged(_nextMoveSprite)) {
            removeSprite(_cursor);
            removeSprite(_nextMoveSprite);
        }
    }
    
    /**
     * Update game character sprites and gives them knowledge
     * of whether or not they are player controlled (AND if it's this client's player)
     * NOTE: 	This is useful in GameCharacterSprite, if it doesn't fix the player colors.
     * 			I don't do this anymore, but it's still in here in case I change my mind.
     *
     * @param isOurTurn the is our turn
     * @param currentPlayerName the current player name
     */
    public void updateGameCharacterSprites(Boolean isOurTurn, Name currentPlayerName)
    {
        for (GameCharacterSprite gameCharacter : _sprites.values() ) {
        	//Log.info("updating sprite:"+gameCharacter._character.characterId);
        	gameCharacter.updateGameCharacter(gameCharacter._character);
	    	if( gameCharacter._character.playerOwner.equals(currentPlayerName)){
	    		gameCharacter._character.isCurrentPlayer = true;
	    		if(isOurTurn == true){
	    			gameCharacter._character.isControllable = true;
	    		} else {
	    			gameCharacter._character.isControllable = false;
	    		}
			} else {
				gameCharacter._character.isCurrentPlayer = false;
			}
        }
    }
    
    // from interface PlaceView
    public void willEnterPlace (PlaceObject plobj)
    {
        _gameobj = (SurvivalObject)plobj;
        _gameobj.addListener(this);
        
        // create sprites for all pieces currently on the board
        for (SurvivalObject.GameCharacter gameCharacter : _gameobj.gameCharacters) {
            addPieceSprite(gameCharacter);
        }
        
        //make sure the placeholder sprites have the right names and types 
        //so that they get colored correctly
    	_nextMoveSprite._character.ownerType = SurvivalObject.ENEMY_ID;
        _nextMoveSprite._character.playerOwner = new Name(SurvivalObject.PLACEHOLDER_NAME);
        _cursor._character.playerOwner = new Name(SurvivalObject.PLACEHOLDER_NAME);
        
    }

    // from interface PlaceView
    public void didLeavePlace (PlaceObject plobj)
    {
        _gameobj.removeListener(this);
        _gameobj = null;
    }

    // from interface SetListener
    public void entryAdded (EntryAddedEvent event)
    {
		// add a sprite for the newly created piece
		addPieceSprite((SurvivalObject.GameCharacter)event.getEntry());
    }

    // from interface SetListener
    public void entryUpdated (EntryUpdatedEvent event)
    {
		if( event.getEntry() instanceof SurvivalObject.GameCharacter ){
			SurvivalObject.GameCharacter gameCharacter = (SurvivalObject.GameCharacter)event.getEntry();
			_sprites.get(gameCharacter.getKey()).updateGameCharacter(gameCharacter);
		}
   }

    // from interface SetListener
    public void entryRemoved (EntryRemovedEvent event)
    {
    	Log.info("entryRemoved");
    	_sprites.remove(event.getKey());
    }

    /**
     * Adds a sprite to the board for the supplied piece.
     */
    protected void addPieceSprite (SurvivalObject.GameCharacter gameCharacter)
    {
        GameCharacterSprite sprite = new GameCharacterSprite(gameCharacter);
        _sprites.put(gameCharacter.getKey(), sprite);
        addSprite(sprite);
    }

    @Override // from JComponent
    public Dimension getPreferredSize ()
    {
      
    	return new Dimension(_size.width * GameCharacterSprite.SIZE + 1,
                           _size.height * GameCharacterSprite.SIZE + 1);
    }   
    
    @Override // from MediaPanel
    protected void paintBehind (Graphics2D gfx, Rectangle dirtyRect)
    {
        super.paintBehind(gfx, dirtyRect);

        // fill in our background color
        gfx.setColor(Color.lightGray);
        gfx.fill(dirtyRect);

        gfx.setColor(Color.darkGray);        
        //Log.info("_mineLocations.size():"+_mineLocations.size());
        if(_gameobj._mineLocations != null){
        	for( int i = 0 ; i < _gameobj._mineLocations.length ; i++){
        		if(i%2 == 0){
        			int Xlocation = _gameobj._mineLocations[i];
        			int Ylocation = _gameobj._mineLocations[i+1];
        			//Log.info("location .x: "+location.x+" location .y: "+location.y);
        			gfx.fillRect(Xlocation*GameCharacterSprite.SIZE, Ylocation*GameCharacterSprite.SIZE, GameCharacterSprite.SIZE, GameCharacterSprite.SIZE);
        		}
        	}
        }
        // draw our grid
        gfx.setColor(Color.black);
        for (int yy = 0; yy <= _size.height; yy++) {
            int ypos = yy * GameCharacterSprite.SIZE;
            gfx.drawLine(0, ypos, GameCharacterSprite.SIZE * _size.width, ypos);
        }
        for (int xx = 0; xx <= _size.width; xx++) {
            int xpos = xx * GameCharacterSprite.SIZE;
            gfx.drawLine(xpos, 0, xpos, GameCharacterSprite.SIZE * _size.height);
        }
    }    
    
    /**
     * Makes the panel display information about a character
     *
     * @param gameCharacter the game character
     */
    public void showUnitInfo(SurvivalObject.GameCharacter gameCharacter)
    {
    	if(gameCharacter != null){
    		_ctrl._panel.unitInfoDetails.setText(
    				"Game Character: " + gameCharacter.playerOwner + "\n" +
    				"Current Health: " + gameCharacter.currentHealth + "\n" +
    				"Current Damage: " + gameCharacter.currentDamage + "\n" +
    				"Current Kill Streak: " + gameCharacter.killStreak + "\n" +
    				"Total Deaths: " + gameCharacter.deaths
    		);
    	} else {
    		_ctrl._panel.unitInfoDetails.setText( "No unit selected \n \n \n \n");
    	}
    }
    
    /**
     * Sets the current turn and the player's health indicators in the panel
     */
    public void updateSidePanelInformation()
    {
    	_ctrl._panel.turnCountLabel.setText("Turn "+_gameobj.currentTurn+" of "+_gameobj.totalGameTurns);
    	SurvivalObject.GameCharacter currentPlayerCharacter = _gameobj.getGameCharacterFromName(_ctx.getClient().getClientObject().username);
    	_ctrl._panel.currentHealth.setText("You ("+currentPlayerCharacter.playerOwner+") have "+currentPlayerCharacter.currentHealth+" health.");
    }

    //
    /**
     * Update side panel options and enables/disables appropriate action options
     *
     * @param characterToTestActions the character to test actions
     */
    public void updateSidePanelOptions(SurvivalObject.GameCharacter characterToTestActions)
    {
    	if(characterToTestActions == null){
    		characterToTestActions = _gameobj.getGameCharacterFromName(_ctx.getClient().getClientObject().username);
    	}
    	if(_logic.canMine(characterToTestActions, _gameobj._mineLocations) ){
    		_ctrl._panel.mineButton.setEnabled(true);
    		_ctrl._panel.mineButton.setText("Mine");
    	} else {
    		_ctrl._panel.mineButton.setEnabled(false);
    		_ctrl._panel.mineButton.setText("Can't Mine");
    	}
    	if(_logic.canAttack(_gameobj.gameCharacters, characterToTestActions) ){
    		_ctrl._panel.attackButton.setEnabled(true);
    		_ctrl._panel.attackButton.setText("Attack");
    	} else {
    		_ctrl._panel.attackButton.setEnabled(false);
    		_ctrl._panel.attackButton.setText("Can't Attack");
    	}
    }
    
    /**
     * After a character dies, the _nextMoveSprite can be in odd places, 
     * which won't let you press an action button until you click a space to move to
     * 
     */
    public void setPlaceHolderPosition()
    {
        SurvivalObject.GameCharacter playerCharacter = _gameobj.getGameCharacterFromName(_ctx.getClient().getClientObject().username);
        _nextMoveSprite.setPosition(playerCharacter.x,playerCharacter.y, true);
    }
    
    /**
     * Shows text in the mediaPanel with the 
     * current messages that are held in _gameobj.matchPraise 
     */
    public void showCurrentMessages()
    {
        SurvivalObject.MatchPraise thisPraise = _gameobj.matchPraise;
        if(thisPraise != null){
        	showMessage(thisPraise.row, thisPraise.col, 
        			thisPraise.score, thisPraise.text);
        }
    }
    
    /**
     * Shows a message on the mediaPanel
     *
     * @param row the row
     * @param col the col
     * @param score the score
     * @param praise the praise
     * 
     */
    
    protected void showMessage(int row, int col, int score, String praise) {
        int x = col * GameCharacterSprite.SIZE;
        int y = row * GameCharacterSprite.SIZE;
         
        int fontsize = 20;

        //TODO: createLabel is deprecated, find what to replace it with.
        Label la = ScoreAnimation.createLabel(praise,
                Color.yellow,
                new Font("Arial", Font.BOLD,
                fontsize),
                this);
        ScoreAnimation sa = new ScoreAnimation(la, x,
                y, praise.length()*100L);
        addAnimation(sa);
        _anim_arranger.positionAvoidAnimation(sa,_board_bounds);
    }   
    
    /* (non-Javadoc)
     * @see com.threerings.presents.dobj.AttributeChangeListener#attributeChanged(com.threerings.presents.dobj.AttributeChangedEvent)
     * 
     * This method reacts any changes on the _gameobj, 
     * and triggers messages to be shown on the client 
     * if the dummy variable NotifyClients was changed.
     */
    public void attributeChanged(AttributeChangedEvent event) {
    	if (SurvivalObject.NOTIFY_CLIENTS.equals(event.getName()))
        {
            showMessage(_gameobj.matchPraise.row, _gameobj.matchPraise.col, _gameobj.matchPraise.score, _gameobj.matchPraise.text);
        }
    }    

    /** Provides access to client services. */
    protected ToyBoxContext _ctx;

    /** A reference to our game object. */
    protected SurvivalObject _gameobj;
    
    /** The size of the Reversi board. */
    protected Dimension _size = new Dimension(SurvivalObject.GAME_SIZE,SurvivalObject.GAME_SIZE);
    
    /** The game controller to which we dispatch user actions. */
    protected SurvivalController _ctrl;    
    
    /** Used to determine legal moves. */
    protected SurvivalLogic _logic;
    
    /** Displays a cursor when we're allowing the user to place a piece. */
    protected CursorSprite _cursor = new CursorSprite();    

    /** Displays a temporary sprite where the user clicked */
    protected CursorSprite _nextMoveSprite = new CursorSprite();  

    /** Contains a mapping from piece id to the sprite for that piece. */
    protected HashMap<Comparable,GameCharacterSprite> _sprites =
    	new HashMap<Comparable,GameCharacterSprite>();

    /** keeps ScoreAnimations spaced */
    private AnimationArranger _anim_arranger = new AnimationArranger();
    private Rectangle  _board_bounds =new Rectangle(0, 0, 450, 600);
    
}